using System;
using MyReactivity;
using MyReactivity.Abstractions;
using Project.Models.Abstractions;

namespace Project.Models
{
    public class Slot : IModel, IReadOnlySlot
    {
        private readonly ItemConfigsStorage _itemConfigsStorage;
        private readonly ReactiveProperty<int> _itemId;
        private readonly ReactiveProperty<int> _quantityOfItems;
        private readonly ReactiveProperty<bool> _isEmpty;
        private readonly ReactiveProperty<bool> _isFull;

        public Slot(ItemConfigsStorage itemConfigsStorage)
        {
            _itemConfigsStorage = itemConfigsStorage;
            _itemId = new ReactiveProperty<int>(0);
            _quantityOfItems = new ReactiveProperty<int>(0);
            _isEmpty = new ReactiveProperty<bool>(true);
            _isFull = new ReactiveProperty<bool>(false);
        }
        public Slot(int itemId, int quantityOfItems, ItemConfigsStorage itemConfigsStorage)
        {
            _itemConfigsStorage = itemConfigsStorage;
            _itemId = new ReactiveProperty<int>();
            _quantityOfItems = new ReactiveProperty<int>();
            _isEmpty = new ReactiveProperty<bool>();
            _isFull = new ReactiveProperty<bool>();

            Set(itemId, quantityOfItems);
        }

        public IReadOnlyReactiveProperty<int> ItemId => _itemId;
        public IReadOnlyReactiveProperty<int> QuantityOfItems => _quantityOfItems;
        public IReadOnlyReactiveProperty<bool> IsEmpty => _isEmpty;
        public IReadOnlyReactiveProperty<bool> IsFull => _isFull;

        public void Set(IReadOnlySlot slot) => 
            Set(slot.ItemId.Value, slot.QuantityOfItems.Value);
        public int Set(int quantityOfItems) => 
            Set(_itemId.Value, quantityOfItems);
        public int Set(int itemId, int quantityOfItems)
        {
            itemId = Math.Clamp(itemId, 0, int.MaxValue);
            quantityOfItems = Math.Clamp(quantityOfItems, 0, int.MaxValue);

            _isEmpty.Value = itemId == 0 || quantityOfItems == 0;
            _isFull.Value = !_isEmpty.Value && quantityOfItems == _itemConfigsStorage[itemId].MaxQuantity;
            _itemId.Value = _isEmpty.Value ? 0 : itemId;
            _quantityOfItems.Value = 0;

            return _isEmpty.Value ? 0 : Add(quantityOfItems);
        }

        public Slot Copy() => 
            new Slot(_itemId.Value, _quantityOfItems.Value, _itemConfigsStorage);

        public void CopyTo(Slot slot) => 
            slot.Set(this);

        public void Clear()
        {
            _isEmpty.Value = true;
            _isFull.Value = false;
            _itemId.Value = 0;
            _quantityOfItems.Value = 0;
        }

        public int Add(int quantityOfItemsToAdd)
        {
            quantityOfItemsToAdd = Math.Clamp(quantityOfItemsToAdd, 0, int.MaxValue);

            if (_isEmpty.Value || quantityOfItemsToAdd == 0)
                return quantityOfItemsToAdd;

            var itemConfig = _itemConfigsStorage[_itemId.Value];
            var totalQuantityOfItems = _quantityOfItems.Value + quantityOfItemsToAdd;

            if(totalQuantityOfItems < itemConfig.MaxQuantity)
            {
                _isFull.Value = false;
                _quantityOfItems.Value += quantityOfItemsToAdd;

                return 0;
            }

            var remainingQuantityOfItems = totalQuantityOfItems - itemConfig.MaxQuantity;

            _isFull.Value = true;
            _quantityOfItems.Value = itemConfig.MaxQuantity;

            return remainingQuantityOfItems;
        }
        public int Add(int itemId, int quantityOfItemsToAdd)
        {
            if (_isEmpty.Value || _itemId.Value != itemId) 
                return Set(itemId, quantityOfItemsToAdd);
            return Add(quantityOfItemsToAdd);
        }

        public int Remove(int quantityOfItemsToRemove)
        {
            quantityOfItemsToRemove = Math.Clamp(quantityOfItemsToRemove, 0, int.MaxValue);

            if (_isEmpty.Value || quantityOfItemsToRemove == 0)
                return quantityOfItemsToRemove;

            if (_quantityOfItems.Value > quantityOfItemsToRemove)
            {
                _quantityOfItems.Value -= quantityOfItemsToRemove;

                return 0;
            }

            var remainingQuantityOfItemsToRemove = quantityOfItemsToRemove - _quantityOfItems.Value;

            Clear();

            return remainingQuantityOfItemsToRemove;
        }
    }
}
